﻿using System;
using System.Collections.Generic;
using DslModeling=global::Microsoft.VisualStudio.Modeling;
using DslDiagrams=global::Microsoft.VisualStudio.Modeling.Diagrams;
using DslValidation=global::Microsoft.VisualStudio.Modeling.Validation;

namespace Salamanca.Sdml
{

    [DslValidation.ValidationState(DslValidation.ValidationState.Enabled)]
    partial class Generalization
    {
        [DslValidation.ValidationMethod(DslValidation.ValidationCategories.Load)]
        private void ValidateNonCyclical(DslValidation.ValidationContext context)
        {
            string message=string.Empty;
            if (!InheritanceAddRule.TestValidInheritance(this.SubEntity, this.Parent, ref message))
                context.LogError(message, "SDML01", this);
        }
    }

    static partial class GeneralizationBuilder
    {
        private static bool CanAcceptDomainEntityAsTarget(DomainEntity candidate)
        {
            return candidate.Parent==null;
        }

        private static bool CanAcceptDomainEntityAndDomainEntityAsSourceAndTarget(DomainEntity sourceDomainEntity, DomainEntity targetDomainEntity)
        {
            return targetDomainEntity.Parent==null;
        }
    }

    partial class GeneralizationConnectAction
    {
        partial class GeneralizationConnectionType:
            GeneralizationConnectionTypeBase
        {
            /// <summary>Helper method to skip from compartment shapes up to their parents</summary>
            private static DslDiagrams.ShapeElement RemovePassThroughShapes(DslDiagrams.ShapeElement shape)
            {
                if (shape is DslDiagrams.Compartment)
                    return shape.ParentShape;

                DslDiagrams.SwimlaneShape swimlane=shape as DslDiagrams.SwimlaneShape;
                if (swimlane!=null && swimlane.ForwardDragDropToParent)
                    return shape.ParentShape;

                return shape;
            }

            /// <summary>Only allow connections that don't create a cycle in the inheritance chain.</summary>
            public override bool CanCreateConnection(DslDiagrams.ShapeElement sourceShapeElement, DslDiagrams.ShapeElement targetShapeElement, ref string connectionWarning)
            {
                DslDiagrams.ShapeElement sourceShape=RemovePassThroughShapes(sourceShapeElement);
                DslDiagrams.ShapeElement targetShape=RemovePassThroughShapes(targetShapeElement);

                if (sourceShape!=null&&targetShape!=null)
                {
                    DomainEntity sourceEntity=sourceShape.Subject as DomainEntity;
                    DomainEntity targetEntity=targetShape.Subject as DomainEntity;
                    if (!InheritanceAddRule.TestValidInheritance(sourceEntity, targetEntity, ref connectionWarning))
                    {
                        return false;
                    }
                }

                // Fall through to the base test if we haven't detected a cycle.
                return base.CanCreateConnection(sourceShapeElement, targetShapeElement, ref connectionWarning);
            }
        }

    }

    [DslModeling.RuleOn(typeof(Generalization), FireTime=DslModeling.TimeToFire.TopLevelCommit, InitiallyDisabled=true)]
    internal sealed class InheritanceAddRule:
        DslModeling.AddRule
    {
        public override void ElementAdded(DslModeling.ElementAddedEventArgs e)
        {
            string message=string.Empty;

            Generalization g=e.ModelElement as Generalization;
            if (g!=null)
                if (!InheritanceAddRule.TestValidInheritance(g.SubEntity, g.Parent, ref message))
                    throw new InvalidOperationException(message);

            base.ElementAdded(e);
        }

        /// <summary>Helper method returns true if the proposed inheritance is valid.</summary>
        /// <remarks>Valid is defined as non-cyclical and non-reflexive</remarks>
        internal static bool TestValidInheritance(DomainEntity sourceEntity, DomainEntity targetEntity, ref string errorMessage)
        {
            if (sourceEntity!=null && targetEntity!=null)
            {
                if (object.Equals(sourceEntity, targetEntity))
                {
                    errorMessage=Resources.SR.InheritanceCannotBeReflexive;
                    return false;
                }

                if (sourceEntity.InheritanceModifier==InheritanceModifier.Sealed)
                {
                    errorMessage=Resources.SR.InheritanceSealedCannotHaveChildren;
                    return false;
                }

                // Loop until we detect an exisiting loop or the root of the hierarchy.
                DomainEntity current=targetEntity.Parent;
                while (current!=null && current!=targetEntity)
                {
                    if (object.Equals(current, sourceEntity))
                    {
                        errorMessage=Resources.SR.InheritanceLoopDetected;
                        return false;
                    }
                    current=current.Parent;
                }
            }

            return true;
        }

    }

}
